
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_MMAP_HEADER
#define MIO_MMAP_HEADER

// #include "mio/page.hpp"
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_PAGE_HEADER
#define MIO_PAGE_HEADER

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

namespace mio {

/**
 * This is used by `basic_mmap` to determine whether to create a read-only or
 * a read-write memory mapping.
 */
enum class access_mode { read, write };

/**
 * Determines the operating system's page allocation granularity.
 *
 * On the first call to this function, it invokes the operating system specific syscall
 * to determine the page size, caches the value, and returns it. Any subsequent call to
 * this function serves the cached value, so no further syscalls are made.
 */
inline size_t page_size() {
  static const size_t page_size = [] {
#ifdef _WIN32
    SYSTEM_INFO SystemInfo;
    GetSystemInfo(&SystemInfo);
    return SystemInfo.dwAllocationGranularity;
#else
    return sysconf(_SC_PAGE_SIZE);
#endif
  }();
  return page_size;
}

/**
 * Alligns `offset` to the operating's system page size such that it subtracts the
 * difference until the nearest page boundary before `offset`, or does nothing if
 * `offset` is already page aligned.
 */
inline size_t make_offset_page_aligned(size_t offset) noexcept {
  const size_t page_size_ = page_size();
  // Use integer division to round down to the nearest page alignment.
  return offset / page_size_ * page_size_;
}

} // namespace mio

#endif // MIO_PAGE_HEADER

#include <cstdint>
#include <iterator>
#include <string>
#include <system_error>

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#include <windows.h>
#else  // ifdef _WIN32
#define INVALID_HANDLE_VALUE -1
#endif // ifdef _WIN32

namespace mio {

// This value may be provided as the `length` parameter to the constructor or
// `map`, in which case a memory mapping of the entire file is created.
enum { map_entire_file = 0 };

#ifdef _WIN32
using file_handle_type = HANDLE;
#else
using file_handle_type = int;
#endif

// This value represents an invalid file handle type. This can be used to
// determine whether `basic_mmap::file_handle` is valid, for example.
const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE;

template <access_mode AccessMode, typename ByteT> struct basic_mmap {
  using value_type = ByteT;
  using size_type = size_t;
  using reference = value_type &;
  using const_reference = const value_type &;
  using pointer = value_type *;
  using const_pointer = const value_type *;
  using difference_type = std::ptrdiff_t;
  using iterator = pointer;
  using const_iterator = const_pointer;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  using iterator_category = std::random_access_iterator_tag;
  using handle_type = file_handle_type;

  static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");

private:
  // Points to the first requested byte, and not to the actual start of the mapping.
  pointer data_ = nullptr;

  // Length--in bytes--requested by user (which may not be the length of the
  // full mapping) and the length of the full mapping.
  size_type length_ = 0;
  size_type mapped_length_ = 0;

  // Letting user map a file using both an existing file handle and a path
  // introcudes some complexity (see `is_handle_internal_`).
  // On POSIX, we only need a file handle to create a mapping, while on
  // Windows systems the file handle is necessary to retrieve a file mapping
  // handle, but any subsequent operations on the mapped region must be done
  // through the latter.
  handle_type file_handle_ = INVALID_HANDLE_VALUE;
#ifdef _WIN32
  handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
#endif

  // Letting user map a file using both an existing file handle and a path
  // introcudes some complexity in that we must not close the file handle if
  // user provided it, but we must close it if we obtained it using the
  // provided path. For this reason, this flag is used to determine when to
  // close `file_handle_`.
  bool is_handle_internal_;

public:
  /**
   * The default constructed mmap object is in a non-mapped state, that is,
   * any operation that attempts to access nonexistent underlying data will
   * result in undefined behaviour/segmentation faults.
   */
  basic_mmap() = default;

#ifdef __cpp_exceptions
  /**
   * The same as invoking the `map` function, except any error that may occur
   * while establishing the mapping is wrapped in a `std::system_error` and is
   * thrown.
   */
  template <typename String>
  basic_mmap(const String &path, const size_type offset = 0,
             const size_type length = map_entire_file) {
    std::error_code error;
    map(path, offset, length, error);
    if (error) {
      throw std::system_error(error);
    }
  }

  /**
   * The same as invoking the `map` function, except any error that may occur
   * while establishing the mapping is wrapped in a `std::system_error` and is
   * thrown.
   */
  basic_mmap(const handle_type handle, const size_type offset = 0,
             const size_type length = map_entire_file) {
    std::error_code error;
    map(handle, offset, length, error);
    if (error) {
      throw std::system_error(error);
    }
  }
#endif // __cpp_exceptions

  /**
   * `basic_mmap` has single-ownership semantics, so transferring ownership
   * may only be accomplished by moving the object.
   */
  basic_mmap(const basic_mmap &) = delete;
  basic_mmap(basic_mmap &&);
  basic_mmap &operator=(const basic_mmap &) = delete;
  basic_mmap &operator=(basic_mmap &&);

  /**
   * If this is a read-write mapping, the destructor invokes sync. Regardless
   * of the access mode, unmap is invoked as a final step.
   */
  ~basic_mmap();

  /**
   * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
   * however, a mapped region of a file gets its own handle, which is returned by
   * 'mapping_handle'.
   */
  handle_type file_handle() const noexcept { return file_handle_; }
  handle_type mapping_handle() const noexcept;

  /** Returns whether a valid memory mapping has been created. */
  bool is_open() const noexcept { return file_handle_ != invalid_handle; }

  /**
   * Returns true if no mapping was established, that is, conceptually the
   * same as though the length that was mapped was 0. This function is
   * provided so that this class has Container semantics.
   */
  bool empty() const noexcept { return length() == 0; }

  /** Returns true if a mapping was established. */
  bool is_mapped() const noexcept;

  /**
   * `size` and `length` both return the logical length, i.e. the number of bytes
   * user requested to be mapped, while `mapped_length` returns the actual number of
   * bytes that were mapped which is a multiple of the underlying operating system's
   * page allocation granularity.
   */
  size_type size() const noexcept { return length(); }
  size_type length() const noexcept { return length_; }
  size_type mapped_length() const noexcept { return mapped_length_; }

  /** Returns the offset relative to the start of the mapping. */
  size_type mapping_offset() const noexcept { return mapped_length_ - length_; }

  /**
   * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
   * exists.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  pointer data() noexcept {
    return data_;
  }
  const_pointer data() const noexcept { return data_; }

  /**
   * Returns an iterator to the first requested byte, if a valid memory mapping
   * exists, otherwise this function call is undefined behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  iterator begin() noexcept {
    return data();
  }
  const_iterator begin() const noexcept { return data(); }
  const_iterator cbegin() const noexcept { return data(); }

  /**
   * Returns an iterator one past the last requested byte, if a valid memory mapping
   * exists, otherwise this function call is undefined behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  iterator end() noexcept {
    return data() + length();
  }
  const_iterator end() const noexcept { return data() + length(); }
  const_iterator cend() const noexcept { return data() + length(); }

  /**
   * Returns a reverse iterator to the last memory mapped byte, if a valid
   * memory mapping exists, otherwise this function call is undefined
   * behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  reverse_iterator rbegin() noexcept {
    return reverse_iterator(end());
  }
  const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
  const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }

  /**
   * Returns a reverse iterator past the first mapped byte, if a valid memory
   * mapping exists, otherwise this function call is undefined behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  reverse_iterator rend() noexcept {
    return reverse_iterator(begin());
  }
  const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
  const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }

  /**
   * Returns a reference to the `i`th byte from the first requested byte (as returned
   * by `data`). If this is invoked when no valid memory mapping has been created
   * prior to this call, undefined behaviour ensues.
   */
  reference operator[](const size_type i) noexcept { return data_[i]; }
  const_reference operator[](const size_type i) const noexcept { return data_[i]; }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `path`, which must be a path to an existing file, is used to retrieve a file
   * handle (which is closed when the object destructs or `unmap` is called), which is
   * then used to memory map the requested region. Upon failure, `error` is set to
   * indicate the reason and the object remains in an unmapped state.
   *
   * `offset` is the number of bytes, relative to the start of the file, where the
   * mapping should begin. When specifying it, there is no need to worry about
   * providing a value that is aligned with the operating system's page allocation
   * granularity. This is adjusted by the implementation such that the first requested
   * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   * `offset` from the start of the file.
   *
   * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   * case a mapping of the entire file is created.
   */
  template <typename String>
  void map(const String &path, const size_type offset, const size_type length,
           std::error_code &error);

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `path`, which must be a path to an existing file, is used to retrieve a file
   * handle (which is closed when the object destructs or `unmap` is called), which is
   * then used to memory map the requested region. Upon failure, `error` is set to
   * indicate the reason and the object remains in an unmapped state.
   *
   * The entire file is mapped.
   */
  template <typename String> void map(const String &path, std::error_code &error) {
    map(path, 0, map_entire_file, error);
  }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is
   * unsuccesful, the reason is reported via `error` and the object remains in
   * a state as if this function hadn't been called.
   *
   * `handle`, which must be a valid file handle, which is used to memory map the
   * requested region. Upon failure, `error` is set to indicate the reason and the
   * object remains in an unmapped state.
   *
   * `offset` is the number of bytes, relative to the start of the file, where the
   * mapping should begin. When specifying it, there is no need to worry about
   * providing a value that is aligned with the operating system's page allocation
   * granularity. This is adjusted by the implementation such that the first requested
   * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   * `offset` from the start of the file.
   *
   * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   * case a mapping of the entire file is created.
   */
  void map(const handle_type handle, const size_type offset, const size_type length,
           std::error_code &error);

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is
   * unsuccesful, the reason is reported via `error` and the object remains in
   * a state as if this function hadn't been called.
   *
   * `handle`, which must be a valid file handle, which is used to memory map the
   * requested region. Upon failure, `error` is set to indicate the reason and the
   * object remains in an unmapped state.
   *
   * The entire file is mapped.
   */
  void map(const handle_type handle, std::error_code &error) {
    map(handle, 0, map_entire_file, error);
  }

  /**
   * If a valid memory mapping has been created prior to this call, this call
   * instructs the kernel to unmap the memory region and disassociate this object
   * from the file.
   *
   * The file handle associated with the file that is mapped is only closed if the
   * mapping was created using a file path. If, on the other hand, an existing
   * file handle was used to create the mapping, the file handle is not closed.
   */
  void unmap();

  void swap(basic_mmap &other);

  /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
  template <access_mode A = AccessMode>
  typename std::enable_if<A == access_mode::write, void>::type sync(std::error_code &error);

  /**
   * All operators compare the address of the first byte and size of the two mapped
   * regions.
   */

private:
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  pointer get_mapping_start() noexcept {
    return !data() ? nullptr : data() - mapping_offset();
  }

  const_pointer get_mapping_start() const noexcept {
    return !data() ? nullptr : data() - mapping_offset();
  }

  /**
   * The destructor syncs changes to disk if `AccessMode` is `write`, but not
   * if it's `read`, but since the destructor cannot be templated, we need to
   * do SFINAE in a dedicated function, where one syncs and the other is a noop.
   */
  template <access_mode A = AccessMode>
  typename std::enable_if<A == access_mode::write, void>::type conditional_sync();
  template <access_mode A = AccessMode>
  typename std::enable_if<A == access_mode::read, void>::type conditional_sync();
};

template <access_mode AccessMode, typename ByteT>
bool operator==(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

template <access_mode AccessMode, typename ByteT>
bool operator!=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

template <access_mode AccessMode, typename ByteT>
bool operator<(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

template <access_mode AccessMode, typename ByteT>
bool operator<=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

template <access_mode AccessMode, typename ByteT>
bool operator>(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

template <access_mode AccessMode, typename ByteT>
bool operator>=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b);

/**
 * This is the basis for all read-only mmap objects and should be preferred over
 * directly using `basic_mmap`.
 */
template <typename ByteT> using basic_mmap_source = basic_mmap<access_mode::read, ByteT>;

/**
 * This is the basis for all read-write mmap objects and should be preferred over
 * directly using `basic_mmap`.
 */
template <typename ByteT> using basic_mmap_sink = basic_mmap<access_mode::write, ByteT>;

/**
 * These aliases cover the most common use cases, both representing a raw byte stream
 * (either with a char or an unsigned char/uint8_t).
 */
using mmap_source = basic_mmap_source<char>;
using ummap_source = basic_mmap_source<unsigned char>;

using mmap_sink = basic_mmap_sink<char>;
using ummap_sink = basic_mmap_sink<unsigned char>;

/**
 * Convenience factory method that constructs a mapping for any `basic_mmap` or
 * `basic_mmap` type.
 */
template <typename MMap, typename MappingToken>
MMap make_mmap(const MappingToken &token, int64_t offset, int64_t length, std::error_code &error) {
  MMap mmap;
  mmap.map(token, offset, length, error);
  return mmap;
}

/**
 * Convenience factory method.
 *
 * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
 * `std::filesystem::path`, `std::vector<char>`, or similar), or a
 * `mmap_source::handle_type`.
 */
template <typename MappingToken>
mmap_source make_mmap_source(const MappingToken &token, mmap_source::size_type offset,
                             mmap_source::size_type length, std::error_code &error) {
  return make_mmap<mmap_source>(token, offset, length, error);
}

template <typename MappingToken>
mmap_source make_mmap_source(const MappingToken &token, std::error_code &error) {
  return make_mmap_source(token, 0, map_entire_file, error);
}

/**
 * Convenience factory method.
 *
 * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
 * `std::filesystem::path`, `std::vector<char>`, or similar), or a
 * `mmap_sink::handle_type`.
 */
template <typename MappingToken>
mmap_sink make_mmap_sink(const MappingToken &token, mmap_sink::size_type offset,
                         mmap_sink::size_type length, std::error_code &error) {
  return make_mmap<mmap_sink>(token, offset, length, error);
}

template <typename MappingToken>
mmap_sink make_mmap_sink(const MappingToken &token, std::error_code &error) {
  return make_mmap_sink(token, 0, map_entire_file, error);
}

} // namespace mio

// #include "detail/mmap.ipp"
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_BASIC_MMAP_IMPL
#define MIO_BASIC_MMAP_IMPL

// #include "mio/mmap.hpp"

// #include "mio/page.hpp"

// #include "mio/detail/string_util.hpp"
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_STRING_UTIL_HEADER
#define MIO_STRING_UTIL_HEADER

#include <type_traits>

namespace mio {
namespace detail {

template <typename S, typename C = typename std::decay<S>::type,
          typename = decltype(std::declval<C>().data()),
          typename = typename std::enable_if<std::is_same<typename C::value_type, char>::value
#ifdef _WIN32
                                             || std::is_same<typename C::value_type, wchar_t>::value
#endif
                                             >::type>
struct char_type_helper {
  using type = typename C::value_type;
};

template <class T> struct char_type {
  using type = typename char_type_helper<T>::type;
};

// TODO: can we avoid this brute force approach?
template <> struct char_type<char *> {
  using type = char;
};

template <> struct char_type<const char *> {
  using type = char;
};

template <size_t N> struct char_type<char[N]> {
  using type = char;
};

template <size_t N> struct char_type<const char[N]> {
  using type = char;
};

#ifdef _WIN32
template <> struct char_type<wchar_t *> {
  using type = wchar_t;
};

template <> struct char_type<const wchar_t *> {
  using type = wchar_t;
};

template <size_t N> struct char_type<wchar_t[N]> {
  using type = wchar_t;
};

template <size_t N> struct char_type<const wchar_t[N]> {
  using type = wchar_t;
};
#endif // _WIN32

template <typename CharT, typename S> struct is_c_str_helper {
  static constexpr bool value =
      std::is_same<CharT *,
                   // TODO: I'm so sorry for this... Can this be made cleaner?
                   typename std::add_pointer<typename std::remove_cv<typename std::remove_pointer<
                       typename std::decay<S>::type>::type>::type>::type>::value;
};

template <typename S> struct is_c_str {
  static constexpr bool value = is_c_str_helper<char, S>::value;
};

#ifdef _WIN32
template <typename S> struct is_c_wstr {
  static constexpr bool value = is_c_str_helper<wchar_t, S>::value;
};
#endif // _WIN32

template <typename S> struct is_c_str_or_c_wstr {
  static constexpr bool value = is_c_str<S>::value
#ifdef _WIN32
                                || is_c_wstr<S>::value
#endif
      ;
};

template <typename String, typename = decltype(std::declval<String>().data()),
          typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type>
const typename char_type<String>::type *c_str(const String &path) {
  return path.data();
}

template <typename String, typename = decltype(std::declval<String>().empty()),
          typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type>
bool empty(const String &path) {
  return path.empty();
}

template <typename String,
          typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type>
const typename char_type<String>::type *c_str(String path) {
  return path;
}

template <typename String,
          typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type>
bool empty(String path) {
  return !path || (*path == 0);
}

} // namespace detail
} // namespace mio

#endif // MIO_STRING_UTIL_HEADER

#include <algorithm>

#ifndef _WIN32
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

namespace mio {
namespace detail {

#ifdef _WIN32
namespace win {

/** Returns the 4 upper bytes of an 8-byte integer. */
inline DWORD int64_high(int64_t n) noexcept { return n >> 32; }

/** Returns the 4 lower bytes of an 8-byte integer. */
inline DWORD int64_low(int64_t n) noexcept { return n & 0xffffffff; }

template <typename String, typename = typename std::enable_if<
                               std::is_same<typename char_type<String>::type, char>::value>::type>
file_handle_type open_file_helper(const String &path, const access_mode mode) {
  return ::CreateFileA(
      c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
}

template <typename String>
typename std::enable_if<std::is_same<typename char_type<String>::type, wchar_t>::value,
                        file_handle_type>::type
open_file_helper(const String &path, const access_mode mode) {
  return ::CreateFileW(
      c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
}

} // namespace win
#endif // _WIN32

/**
 * Returns the last platform specific system error (errno on POSIX and
 * GetLastError on Win) as a `std::error_code`.
 */
inline std::error_code last_error() noexcept {
  std::error_code error;
#ifdef _WIN32
  error.assign(GetLastError(), std::system_category());
#else
  error.assign(errno, std::system_category());
#endif
  return error;
}

template <typename String>
file_handle_type open_file(const String &path, const access_mode mode, std::error_code &error) {
  error.clear();
  if (detail::empty(path)) {
    error = std::make_error_code(std::errc::invalid_argument);
    return invalid_handle;
  }
#ifdef _WIN32
  const auto handle = win::open_file_helper(path, mode);
#else // POSIX
  const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR);
#endif
  if (handle == invalid_handle) {
    error = detail::last_error();
  }
  return handle;
}

inline size_t query_file_size(file_handle_type handle, std::error_code &error) {
  error.clear();
#ifdef _WIN32
  LARGE_INTEGER file_size;
  if (::GetFileSizeEx(handle, &file_size) == 0) {
    error = detail::last_error();
    return 0;
  }
  return static_cast<int64_t>(file_size.QuadPart);
#else // POSIX
  struct stat sbuf;
  if (::fstat(handle, &sbuf) == -1) {
    error = detail::last_error();
    return 0;
  }
  return sbuf.st_size;
#endif
}

struct mmap_context {
  char *data;
  int64_t length;
  int64_t mapped_length;
#ifdef _WIN32
  file_handle_type file_mapping_handle;
#endif
};

inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset,
                               const int64_t length, const access_mode mode,
                               std::error_code &error) {
  const int64_t aligned_offset = make_offset_page_aligned(offset);
  const int64_t length_to_map = offset - aligned_offset + length;
#ifdef _WIN32
  const int64_t max_file_size = offset + length;
  const auto file_mapping_handle = ::CreateFileMapping(
      file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
      win::int64_high(max_file_size), win::int64_low(max_file_size), 0);
  if (file_mapping_handle == invalid_handle) {
    error = detail::last_error();
    return {};
  }
  char *mapping_start = static_cast<char *>(::MapViewOfFile(
      file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
      win::int64_high(aligned_offset), win::int64_low(aligned_offset), length_to_map));
  if (mapping_start == nullptr) {
    // Close file handle if mapping it failed.
    ::CloseHandle(file_mapping_handle);
    error = detail::last_error();
    return {};
  }
#else // POSIX
  char *mapping_start =
      static_cast<char *>(::mmap(0, // Don't give hint as to where to map.
                                 length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE,
                                 MAP_SHARED, file_handle, aligned_offset));
  if (mapping_start == MAP_FAILED) {
    error = detail::last_error();
    return {};
  }
#endif
  mmap_context ctx;
  ctx.data = mapping_start + offset - aligned_offset;
  ctx.length = length;
  ctx.mapped_length = length_to_map;
#ifdef _WIN32
  ctx.file_mapping_handle = file_mapping_handle;
#endif
  return ctx;
}

} // namespace detail

// -- basic_mmap --

template <access_mode AccessMode, typename ByteT> basic_mmap<AccessMode, ByteT>::~basic_mmap() {
  conditional_sync();
  unmap();
}

template <access_mode AccessMode, typename ByteT>
basic_mmap<AccessMode, ByteT>::basic_mmap(basic_mmap &&other)
    : data_(std::move(other.data_)), length_(std::move(other.length_)),
      mapped_length_(std::move(other.mapped_length_)), file_handle_(std::move(other.file_handle_))
#ifdef _WIN32
      ,
      file_mapping_handle_(std::move(other.file_mapping_handle_))
#endif
      ,
      is_handle_internal_(std::move(other.is_handle_internal_)) {
  other.data_ = nullptr;
  other.length_ = other.mapped_length_ = 0;
  other.file_handle_ = invalid_handle;
#ifdef _WIN32
  other.file_mapping_handle_ = invalid_handle;
#endif
}

template <access_mode AccessMode, typename ByteT>
basic_mmap<AccessMode, ByteT> &basic_mmap<AccessMode, ByteT>::operator=(basic_mmap &&other) {
  if (this != &other) {
    // First the existing mapping needs to be removed.
    unmap();
    data_ = std::move(other.data_);
    length_ = std::move(other.length_);
    mapped_length_ = std::move(other.mapped_length_);
    file_handle_ = std::move(other.file_handle_);
#ifdef _WIN32
    file_mapping_handle_ = std::move(other.file_mapping_handle_);
#endif
    is_handle_internal_ = std::move(other.is_handle_internal_);

    // The moved from basic_mmap's fields need to be reset, because
    // otherwise other's destructor will unmap the same mapping that was
    // just moved into this.
    other.data_ = nullptr;
    other.length_ = other.mapped_length_ = 0;
    other.file_handle_ = invalid_handle;
#ifdef _WIN32
    other.file_mapping_handle_ = invalid_handle;
#endif
    other.is_handle_internal_ = false;
  }
  return *this;
}

template <access_mode AccessMode, typename ByteT>
typename basic_mmap<AccessMode, ByteT>::handle_type
basic_mmap<AccessMode, ByteT>::mapping_handle() const noexcept {
#ifdef _WIN32
  return file_mapping_handle_;
#else
  return file_handle_;
#endif
}

template <access_mode AccessMode, typename ByteT>
template <typename String>
void basic_mmap<AccessMode, ByteT>::map(const String &path, const size_type offset,
                                        const size_type length, std::error_code &error) {
  error.clear();
  if (detail::empty(path)) {
    error = std::make_error_code(std::errc::invalid_argument);
    return;
  }
  const auto handle = detail::open_file(path, AccessMode, error);
  if (error) {
    return;
  }

  map(handle, offset, length, error);
  // This MUST be after the call to map, as that sets this to true.
  if (!error) {
    is_handle_internal_ = true;
  }
}

template <access_mode AccessMode, typename ByteT>
void basic_mmap<AccessMode, ByteT>::map(const handle_type handle, const size_type offset,
                                        const size_type length, std::error_code &error) {
  error.clear();
  if (handle == invalid_handle) {
    error = std::make_error_code(std::errc::bad_file_descriptor);
    return;
  }

  const auto file_size = detail::query_file_size(handle, error);
  if (error) {
    return;
  }

  if (offset + length > file_size) {
    error = std::make_error_code(std::errc::invalid_argument);
    return;
  }

  const auto ctx = detail::memory_map(
      handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error);
  if (!error) {
    // We must unmap the previous mapping that may have existed prior to this call.
    // Note that this must only be invoked after a new mapping has been created in
    // order to provide the strong guarantee that, should the new mapping fail, the
    // `map` function leaves this instance in a state as though the function had
    // never been invoked.
    unmap();
    file_handle_ = handle;
    is_handle_internal_ = false;
    data_ = reinterpret_cast<pointer>(ctx.data);
    length_ = ctx.length;
    mapped_length_ = ctx.mapped_length;
#ifdef _WIN32
    file_mapping_handle_ = ctx.file_mapping_handle;
#endif
  }
}

template <access_mode AccessMode, typename ByteT>
template <access_mode A>
typename std::enable_if<A == access_mode::write, void>::type
basic_mmap<AccessMode, ByteT>::sync(std::error_code &error) {
  error.clear();
  if (!is_open()) {
    error = std::make_error_code(std::errc::bad_file_descriptor);
    return;
  }

  if (data()) {
#ifdef _WIN32
    if (::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 ||
        ::FlushFileBuffers(file_handle_) == 0)
#else // POSIX
    if (::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
#endif
    {
      error = detail::last_error();
      return;
    }
  }
#ifdef _WIN32
  if (::FlushFileBuffers(file_handle_) == 0) {
    error = detail::last_error();
  }
#endif
}

template <access_mode AccessMode, typename ByteT> void basic_mmap<AccessMode, ByteT>::unmap() {
  if (!is_open()) {
    return;
  }
  // TODO do we care about errors here?
#ifdef _WIN32
  if (is_mapped()) {
    ::UnmapViewOfFile(get_mapping_start());
    ::CloseHandle(file_mapping_handle_);
  }
#else // POSIX
  if (data_) {
    ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_);
  }
#endif

  // If `file_handle_` was obtained by our opening it (when map is called with
  // a path, rather than an existing file handle), we need to close it,
  // otherwise it must not be closed as it may still be used outside this
  // instance.
  if (is_handle_internal_) {
#ifdef _WIN32
    ::CloseHandle(file_handle_);
#else // POSIX
    ::close(file_handle_);
#endif
  }

  // Reset fields to their default values.
  data_ = nullptr;
  length_ = mapped_length_ = 0;
  file_handle_ = invalid_handle;
#ifdef _WIN32
  file_mapping_handle_ = invalid_handle;
#endif
}

template <access_mode AccessMode, typename ByteT>
bool basic_mmap<AccessMode, ByteT>::is_mapped() const noexcept {
#ifdef _WIN32
  return file_mapping_handle_ != invalid_handle;
#else // POSIX
  return is_open();
#endif
}

template <access_mode AccessMode, typename ByteT>
void basic_mmap<AccessMode, ByteT>::swap(basic_mmap &other) {
  if (this != &other) {
    using std::swap;
    swap(data_, other.data_);
    swap(file_handle_, other.file_handle_);
#ifdef _WIN32
    swap(file_mapping_handle_, other.file_mapping_handle_);
#endif
    swap(length_, other.length_);
    swap(mapped_length_, other.mapped_length_);
    swap(is_handle_internal_, other.is_handle_internal_);
  }
}

template <access_mode AccessMode, typename ByteT>
template <access_mode A>
typename std::enable_if<A == access_mode::write, void>::type
basic_mmap<AccessMode, ByteT>::conditional_sync() {
  // This is invoked from the destructor, so not much we can do about
  // failures here.
  std::error_code ec;
  sync(ec);
}

template <access_mode AccessMode, typename ByteT>
template <access_mode A>
typename std::enable_if<A == access_mode::read, void>::type
basic_mmap<AccessMode, ByteT>::conditional_sync() {
  // noop
}

template <access_mode AccessMode, typename ByteT>
bool operator==(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  return a.data() == b.data() && a.size() == b.size();
}

template <access_mode AccessMode, typename ByteT>
bool operator!=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  return !(a == b);
}

template <access_mode AccessMode, typename ByteT>
bool operator<(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  if (a.data() == b.data()) {
    return a.size() < b.size();
  }
  return a.data() < b.data();
}

template <access_mode AccessMode, typename ByteT>
bool operator<=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  return !(a > b);
}

template <access_mode AccessMode, typename ByteT>
bool operator>(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  if (a.data() == b.data()) {
    return a.size() > b.size();
  }
  return a.data() > b.data();
}

template <access_mode AccessMode, typename ByteT>
bool operator>=(const basic_mmap<AccessMode, ByteT> &a, const basic_mmap<AccessMode, ByteT> &b) {
  return !(a < b);
}

} // namespace mio

#endif // MIO_BASIC_MMAP_IMPL

#endif // MIO_MMAP_HEADER
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_PAGE_HEADER
#define MIO_PAGE_HEADER

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

namespace mio {

/**
 * This is used by `basic_mmap` to determine whether to create a read-only or
 * a read-write memory mapping.
 */
enum class access_mode { read, write };

/**
 * Determines the operating system's page allocation granularity.
 *
 * On the first call to this function, it invokes the operating system specific syscall
 * to determine the page size, caches the value, and returns it. Any subsequent call to
 * this function serves the cached value, so no further syscalls are made.
 */
inline size_t page_size() {
  static const size_t page_size = [] {
#ifdef _WIN32
    SYSTEM_INFO SystemInfo;
    GetSystemInfo(&SystemInfo);
    return SystemInfo.dwAllocationGranularity;
#else
    return sysconf(_SC_PAGE_SIZE);
#endif
  }();
  return page_size;
}

/**
 * Alligns `offset` to the operating's system page size such that it subtracts the
 * difference until the nearest page boundary before `offset`, or does nothing if
 * `offset` is already page aligned.
 */
inline size_t make_offset_page_aligned(size_t offset) noexcept {
  const size_t page_size_ = page_size();
  // Use integer division to round down to the nearest page alignment.
  return offset / page_size_ * page_size_;
}

} // namespace mio

#endif // MIO_PAGE_HEADER
/* Copyright 2017 https://github.com/mandreyel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef MIO_SHARED_MMAP_HEADER
#define MIO_SHARED_MMAP_HEADER

// #include "mio/mmap.hpp"

#include <memory>       // std::shared_ptr
#include <system_error> // std::error_code

namespace mio {

/**
 * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with
 * `std::shared_ptr` semantics.
 *
 * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if
 * shared semantics are not required.
 */
template <access_mode AccessMode, typename ByteT> class basic_shared_mmap {
  using impl_type = basic_mmap<AccessMode, ByteT>;
  std::shared_ptr<impl_type> pimpl_;

public:
  using value_type = typename impl_type::value_type;
  using size_type = typename impl_type::size_type;
  using reference = typename impl_type::reference;
  using const_reference = typename impl_type::const_reference;
  using pointer = typename impl_type::pointer;
  using const_pointer = typename impl_type::const_pointer;
  using difference_type = typename impl_type::difference_type;
  using iterator = typename impl_type::iterator;
  using const_iterator = typename impl_type::const_iterator;
  using reverse_iterator = typename impl_type::reverse_iterator;
  using const_reverse_iterator = typename impl_type::const_reverse_iterator;
  using iterator_category = typename impl_type::iterator_category;
  using handle_type = typename impl_type::handle_type;
  using mmap_type = impl_type;

  basic_shared_mmap() = default;
  basic_shared_mmap(const basic_shared_mmap &) = default;
  basic_shared_mmap &operator=(const basic_shared_mmap &) = default;
  basic_shared_mmap(basic_shared_mmap &&) = default;
  basic_shared_mmap &operator=(basic_shared_mmap &&) = default;

  /** Takes ownership of an existing mmap object. */
  basic_shared_mmap(mmap_type &&mmap) : pimpl_(std::make_shared<mmap_type>(std::move(mmap))) {}

  /** Takes ownership of an existing mmap object. */
  basic_shared_mmap &operator=(mmap_type &&mmap) {
    pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
    return *this;
  }

  /** Initializes this object with an already established shared mmap. */
  basic_shared_mmap(std::shared_ptr<mmap_type> mmap) : pimpl_(std::move(mmap)) {}

  /** Initializes this object with an already established shared mmap. */
  basic_shared_mmap &operator=(std::shared_ptr<mmap_type> mmap) {
    pimpl_ = std::move(mmap);
    return *this;
  }

#ifdef __cpp_exceptions
  /**
   * The same as invoking the `map` function, except any error that may occur
   * while establishing the mapping is wrapped in a `std::system_error` and is
   * thrown.
   */
  template <typename String>
  basic_shared_mmap(const String &path, const size_type offset = 0,
                    const size_type length = map_entire_file) {
    std::error_code error;
    map(path, offset, length, error);
    if (error) {
      throw std::system_error(error);
    }
  }

  /**
   * The same as invoking the `map` function, except any error that may occur
   * while establishing the mapping is wrapped in a `std::system_error` and is
   * thrown.
   */
  basic_shared_mmap(const handle_type handle, const size_type offset = 0,
                    const size_type length = map_entire_file) {
    std::error_code error;
    map(handle, offset, length, error);
    if (error) {
      throw std::system_error(error);
    }
  }
#endif // __cpp_exceptions

  /**
   * If this is a read-write mapping and the last reference to the mapping,
   * the destructor invokes sync. Regardless of the access mode, unmap is
   * invoked as a final step.
   */
  ~basic_shared_mmap() = default;

  /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
  std::shared_ptr<mmap_type> get_shared_ptr() { return pimpl_; }

  /**
   * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
   * however, a mapped region of a file gets its own handle, which is returned by
   * 'mapping_handle'.
   */
  handle_type file_handle() const noexcept {
    return pimpl_ ? pimpl_->file_handle() : invalid_handle;
  }

  handle_type mapping_handle() const noexcept {
    return pimpl_ ? pimpl_->mapping_handle() : invalid_handle;
  }

  /** Returns whether a valid memory mapping has been created. */
  bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }

  /**
   * Returns true if no mapping was established, that is, conceptually the
   * same as though the length that was mapped was 0. This function is
   * provided so that this class has Container semantics.
   */
  bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); }

  /**
   * `size` and `length` both return the logical length, i.e. the number of bytes
   * user requested to be mapped, while `mapped_length` returns the actual number of
   * bytes that were mapped which is a multiple of the underlying operating system's
   * page allocation granularity.
   */
  size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
  size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
  size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; }

  /**
   * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
   * exists.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  pointer data() noexcept {
    return pimpl_->data();
  }
  const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; }

  /**
   * Returns an iterator to the first requested byte, if a valid memory mapping
   * exists, otherwise this function call is undefined behaviour.
   */
  iterator begin() noexcept { return pimpl_->begin(); }
  const_iterator begin() const noexcept { return pimpl_->begin(); }
  const_iterator cbegin() const noexcept { return pimpl_->cbegin(); }

  /**
   * Returns an iterator one past the last requested byte, if a valid memory mapping
   * exists, otherwise this function call is undefined behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  iterator end() noexcept {
    return pimpl_->end();
  }
  const_iterator end() const noexcept { return pimpl_->end(); }
  const_iterator cend() const noexcept { return pimpl_->cend(); }

  /**
   * Returns a reverse iterator to the last memory mapped byte, if a valid
   * memory mapping exists, otherwise this function call is undefined
   * behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  reverse_iterator rbegin() noexcept {
    return pimpl_->rbegin();
  }
  const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); }
  const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); }

  /**
   * Returns a reverse iterator past the first mapped byte, if a valid memory
   * mapping exists, otherwise this function call is undefined behaviour.
   */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  reverse_iterator rend() noexcept {
    return pimpl_->rend();
  }
  const_reverse_iterator rend() const noexcept { return pimpl_->rend(); }
  const_reverse_iterator crend() const noexcept { return pimpl_->crend(); }

  /**
   * Returns a reference to the `i`th byte from the first requested byte (as returned
   * by `data`). If this is invoked when no valid memory mapping has been created
   * prior to this call, undefined behaviour ensues.
   */
  reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; }
  const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `path`, which must be a path to an existing file, is used to retrieve a file
   * handle (which is closed when the object destructs or `unmap` is called), which is
   * then used to memory map the requested region. Upon failure, `error` is set to
   * indicate the reason and the object remains in an unmapped state.
   *
   * `offset` is the number of bytes, relative to the start of the file, where the
   * mapping should begin. When specifying it, there is no need to worry about
   * providing a value that is aligned with the operating system's page allocation
   * granularity. This is adjusted by the implementation such that the first requested
   * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   * `offset` from the start of the file.
   *
   * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   * case a mapping of the entire file is created.
   */
  template <typename String>
  void map(const String &path, const size_type offset, const size_type length,
           std::error_code &error) {
    map_impl(path, offset, length, error);
  }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `path`, which must be a path to an existing file, is used to retrieve a file
   * handle (which is closed when the object destructs or `unmap` is called), which is
   * then used to memory map the requested region. Upon failure, `error` is set to
   * indicate the reason and the object remains in an unmapped state.
   *
   * The entire file is mapped.
   */
  template <typename String> void map(const String &path, std::error_code &error) {
    map_impl(path, 0, map_entire_file, error);
  }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `handle`, which must be a valid file handle, which is used to memory map the
   * requested region. Upon failure, `error` is set to indicate the reason and the
   * object remains in an unmapped state.
   *
   * `offset` is the number of bytes, relative to the start of the file, where the
   * mapping should begin. When specifying it, there is no need to worry about
   * providing a value that is aligned with the operating system's page allocation
   * granularity. This is adjusted by the implementation such that the first requested
   * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   * `offset` from the start of the file.
   *
   * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   * case a mapping of the entire file is created.
   */
  void map(const handle_type handle, const size_type offset, const size_type length,
           std::error_code &error) {
    map_impl(handle, offset, length, error);
  }

  /**
   * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   * reason is reported via `error` and the object remains in a state as if this
   * function hadn't been called.
   *
   * `handle`, which must be a valid file handle, which is used to memory map the
   * requested region. Upon failure, `error` is set to indicate the reason and the
   * object remains in an unmapped state.
   *
   * The entire file is mapped.
   */
  void map(const handle_type handle, std::error_code &error) {
    map_impl(handle, 0, map_entire_file, error);
  }

  /**
   * If a valid memory mapping has been created prior to this call, this call
   * instructs the kernel to unmap the memory region and disassociate this object
   * from the file.
   *
   * The file handle associated with the file that is mapped is only closed if the
   * mapping was created using a file path. If, on the other hand, an existing
   * file handle was used to create the mapping, the file handle is not closed.
   */
  void unmap() {
    if (pimpl_)
      pimpl_->unmap();
  }

  void swap(basic_shared_mmap &other) { pimpl_.swap(other.pimpl_); }

  /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
  template <access_mode A = AccessMode,
            typename = typename std::enable_if<A == access_mode::write>::type>
  void sync(std::error_code &error) {
    if (pimpl_)
      pimpl_->sync(error);
  }

  /** All operators compare the underlying `basic_mmap`'s addresses. */

  friend bool operator==(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return a.pimpl_ == b.pimpl_;
  }

  friend bool operator!=(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return !(a == b);
  }

  friend bool operator<(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return a.pimpl_ < b.pimpl_;
  }

  friend bool operator<=(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return a.pimpl_ <= b.pimpl_;
  }

  friend bool operator>(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return a.pimpl_ > b.pimpl_;
  }

  friend bool operator>=(const basic_shared_mmap &a, const basic_shared_mmap &b) {
    return a.pimpl_ >= b.pimpl_;
  }

private:
  template <typename MappingToken>
  void map_impl(const MappingToken &token, const size_type offset, const size_type length,
                std::error_code &error) {
    if (!pimpl_) {
      mmap_type mmap = make_mmap<mmap_type>(token, offset, length, error);
      if (error) {
        return;
      }
      pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
    } else {
      pimpl_->map(token, offset, length, error);
    }
  }
};

/**
 * This is the basis for all read-only mmap objects and should be preferred over
 * directly using basic_shared_mmap.
 */
template <typename ByteT>
using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, ByteT>;

/**
 * This is the basis for all read-write mmap objects and should be preferred over
 * directly using basic_shared_mmap.
 */
template <typename ByteT>
using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, ByteT>;

/**
 * These aliases cover the most common use cases, both representing a raw byte stream
 * (either with a char or an unsigned char/uint8_t).
 */
using shared_mmap_source = basic_shared_mmap_source<char>;
using shared_ummap_source = basic_shared_mmap_source<unsigned char>;

using shared_mmap_sink = basic_shared_mmap_sink<char>;
using shared_ummap_sink = basic_shared_mmap_sink<unsigned char>;

} // namespace mio

#endif // MIO_SHARED_MMAP_HEADER

#pragma once
#include <utility>

namespace csv2 {

namespace trim_policy {
struct no_trimming {
public:
  static std::pair<size_t, size_t> trim(const char *buffer, size_t start, size_t end) {
    (void)(buffer); // to silence unused parameter warning
    return {start, end};
  }
};

template <char... character_list> struct trim_characters {
private:
  constexpr static bool is_trim_char(char) { return false; }

  template <class... Tail> constexpr static bool is_trim_char(char c, char head, Tail... tail) {
    return c == head || is_trim_char(c, tail...);
  }

public:
  static std::pair<size_t, size_t> trim(const char *buffer, size_t start, size_t end) {
    size_t new_start = start, new_end = end;
    while (new_start != new_end && is_trim_char(buffer[new_start], character_list...))
      ++new_start;
    while (new_start != new_end && is_trim_char(buffer[new_end - 1], character_list...))
      --new_end;
    return {new_start, new_end};
  }
};

using trim_whitespace = trim_characters<' ', '\t'>;
} // namespace trim_policy

template <char character> struct delimiter {
  constexpr static char value = character;
};

template <char character> struct quote_character {
  constexpr static char value = character;
};

template <bool flag> struct first_row_is_header {
  constexpr static bool value = flag;
};

} // namespace csv2
#pragma once
#include <cstring>
#if __has_include("sys/mman.h") ||                                                                 \
                  __has_include(                                                                   \
                      <sys/mman.h>) || __has_include("windows.h") || __has_include(<windows.h>)
#define __CSV2_HAS_MMAN_H__ 1
// #include <csv2/mio.hpp>
#endif
// #include <csv2/parameters.hpp>
#include <istream>
#include <string>
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
#include <string_view>
#endif

namespace csv2 {

template <class delimiter = delimiter<','>, class quote_character = quote_character<'"'>,
          class first_row_is_header = first_row_is_header<true>,
          class trim_policy = trim_policy::trim_whitespace>
class Reader {
#if __CSV2_HAS_MMAN_H__
  mio::mmap_source mmap_;       // mmap source
#endif
  const char *buffer_{nullptr}; // pointer to memory-mapped data
  size_t buffer_size_{0};       // mapped length of buffer
  size_t header_start_{0};      // start index of header (cache)
  size_t header_end_{0};        // end index of header (cache)

public:
#if __CSV2_HAS_MMAN_H__
  // Use this if you'd like to mmap the CSV file
  template <typename StringType> bool mmap(StringType &&filename) {
    mmap_ = mio::mmap_source(filename);
    if (!mmap_.is_open() || !mmap_.is_mapped())
      return false;
    buffer_ = mmap_.data();
    buffer_size_ = mmap_.mapped_length();
    return true;
  }
#endif

  // Use this if you have the CSV contents
  // in an std::string already
  template <typename StringType> bool parse(StringType &&contents) {
    buffer_ = std::forward<StringType>(contents).c_str();
    buffer_size_ = contents.size();
    return buffer_size_ > 0;
  }


  // Use this if you have the CSV contents
  // in an std::string_view already
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
  bool parse_view(std::string_view sv) {
    buffer_ = sv.data();
    buffer_size_ = sv.size();
    return buffer_size_ > 0;
  }
#endif


  class RowIterator;
  class Row;
  class CellIterator;

  class Cell {
    const char *buffer_{nullptr}; // Pointer to memory-mapped buffer
    size_t start_{0};             // Start index of cell content
    size_t end_{0};               // End index of cell content
    bool escaped_{false};         // Does the cell have escaped content?
    friend class Row;
    friend class CellIterator;

  public:
// returns a view on the cell's contents if C++17 available
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
    std::string_view read_view() const {
      const auto new_start_end = trim_policy::trim(buffer_, start_, end_);
      return std::string_view(buffer_ + new_start_end.first,
                              new_start_end.second - new_start_end.first);
    }
#endif
    // Returns the raw_value of the cell without handling escaped
    // content, e.g., cell containing """foo""" will be returned
    // as is
    template <typename Container> void read_raw_value(Container &result) const {
      if (start_ >= end_)
        return;
      result.reserve(end_ - start_);
      for (size_t i = start_; i < end_; ++i)
        result.push_back(buffer_[i]);
    }

    // If cell is escaped, convert and return correct cell contents,
    // e.g., """foo""" => ""foo""
    template <typename Container> void read_value(Container &result) const {
      if (start_ >= end_)
        return;
      result.reserve(end_ - start_);
      const auto new_start_end = trim_policy::trim(buffer_, start_, end_);
      for (size_t i = new_start_end.first; i < new_start_end.second; ++i)
        result.push_back(buffer_[i]);
    }
  };

  class Row {
    const char *buffer_{nullptr}; // Pointer to memory-mapped buffer
    size_t start_{0};             // Start index of row content
    size_t end_{0};               // End index of row content
    friend class RowIterator;
    friend class Reader;

  public:
    // address of row
    const char *address() const { return buffer_; }
	// returns the char length of the row
	size_t length() const { return end_ - start_; }

    // Returns the raw_value of the row
    template <typename Container> void read_raw_value(Container &result) const {
      if (start_ >= end_)
        return;
      result.reserve(end_ - start_);
      for (size_t i = start_; i < end_; ++i)
        result.push_back(buffer_[i]);
    }

    class CellIterator {
      friend class Row;
      const char *buffer_;
      size_t buffer_size_;
      size_t start_;
      size_t current_;
      size_t end_;

    public:
      CellIterator(const char *buffer, size_t buffer_size, size_t start, size_t end)
          : buffer_(buffer), buffer_size_(buffer_size), start_(start), current_(start_), end_(end) {
      }

      CellIterator &operator++() {
        current_ += 1;
        return *this;
      }

      Cell operator*() {
        bool escaped{false};
        class Cell cell;
        cell.buffer_ = buffer_;
        cell.start_ = current_;
        cell.end_ = end_;

        size_t last_quote_location = 0;
        bool quote_opened = false;
        for (auto i = current_; i < end_; i++) {
          current_ = i;
          if (buffer_[i] == delimiter::value && !quote_opened) {
            // actual delimiter
            // end of cell
            cell.end_ = current_;
            cell.escaped_ = escaped;
            return cell;
          } else {
            if (buffer_[i] == quote_character::value) {
              if (!quote_opened) {
                // first quote for this cell
                quote_opened = true;
                last_quote_location = i;
              } else {
                escaped = (last_quote_location == i - 1);
                last_quote_location += (i - last_quote_location) * size_t(!escaped);
                quote_opened = false;
              }
            }
          }
        }
        cell.end_ = current_ + 1;
        return cell;
      }

      bool operator!=(const CellIterator &rhs) { return current_ != rhs.current_; }
    };

    CellIterator begin() const { return CellIterator(buffer_, end_ - start_, start_, end_); }
    CellIterator end() const { return CellIterator(buffer_, end_ - start_, end_, end_); }
  };

  class RowIterator {
    friend class Reader;
    const char *buffer_;
    size_t buffer_size_;
    size_t start_;
    size_t end_;

  public:
    RowIterator(const char *buffer, size_t buffer_size, size_t start)
        : buffer_(buffer), buffer_size_(buffer_size), start_(start), end_(start_) {}

    RowIterator &operator++() {
      start_ = end_ + 1;
      end_ = start_;
      return *this;
    }

    Row operator*() {
      Row result;
      result.buffer_ = buffer_;
      result.start_ = start_;
      result.end_ = end_;

      if (const char *ptr =
              static_cast<const char *>(memchr(&buffer_[start_], '\n', (buffer_size_ - start_)))) {
        end_ = start_ + (ptr - &buffer_[start_]);
        result.end_ = end_;
        start_ = end_ + 1;
      } else {
        // last row
        end_ = buffer_size_;
        result.end_ = end_;
      }
      return result;
    }

    bool operator!=(const RowIterator &rhs) { return start_ != rhs.start_; }
  };

  RowIterator begin() const {
    if (buffer_size_ == 0)
      return end();
    if (first_row_is_header::value) {
      const auto header_indices = header_indices_();
      return RowIterator(buffer_, buffer_size_,
                         header_indices.second > 0 ? header_indices.second + 1 : 0);
    } else {
      return RowIterator(buffer_, buffer_size_, 0);
    }
  }

  RowIterator end() const { return RowIterator(buffer_, buffer_size_, buffer_size_ + 1); }

private:
  std::pair<size_t, size_t> header_indices_() const {
    size_t start = 0, end = 0;

    if (const char *ptr =
            static_cast<const char *>(memchr(&buffer_[start], '\n', (buffer_size_ - start)))) {
      end = start + (ptr - &buffer_[start]);
    }
    return {start, end};
  }

public:
  Row header() const {
    size_t start = 0, end = 0;
    Row result;
    result.buffer_ = buffer_;
    result.start_ = start;
    result.end_ = end;

    if (const char *ptr =
            static_cast<const char *>(memchr(&buffer_[start], '\n', (buffer_size_ - start)))) {
      end = start + (ptr - &buffer_[start]);
      result.end_ = end;
    }
    return result;
  }

  /**
   * @returns The number of rows (excluding the header)
  */
  size_t rows(bool ignore_empty_lines = false) const {
    size_t result{0};
    if (!buffer_ || buffer_size_ == 0)
      return result;
    
    // Count the first row if not header
    if (not first_row_is_header::value
        and (not ignore_empty_lines
        or *(static_cast<const char*>(buffer_)) != '\r'))
      ++result;

    for (const char *p = buffer_
        ; (p = static_cast<const char *>(memchr(p, '\n', (buffer_ + buffer_size_) - p)))
        ; ++p) {
      if (ignore_empty_lines
          and (p >= buffer_ + buffer_size_ - 1
          or *(p + 1) == '\r'))
        continue;
      ++result;
    }
    return result;
  }

  size_t cols() const {
    size_t result{0};
    for (const auto cell : header())
      result += 1;
    return result;
  }
};
} // namespace csv2

#pragma once
#include <cstring>
// #include <csv2/parameters.hpp>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>

namespace csv2 {

template <typename, typename T> struct has_close : std::false_type {};

template <typename C, typename Ret, typename... Args> struct has_close<C, Ret(Args...)> {
private:
  template <typename T>
  static constexpr auto check(T *) ->
      typename std::is_same<decltype(std::declval<T>().close(std::declval<Args>()...)), Ret>::type;

  template <typename> static constexpr std::false_type check(...);

  typedef decltype(check<C>(0)) type;

public:
  static constexpr bool value = type::value;
};

template <class delimiter = delimiter<','>, typename Stream = std::ofstream> class Writer {
  Stream &stream_; // output stream for the writer
public:
  Writer(Stream &stream) : stream_(stream) {}

  ~Writer() {
    if constexpr (has_close<Stream, void()>::value) {
      // has `close`
      stream_.close();
    }
  }

  template <typename Container> void write_row(Container &&row) {
    const auto &strings = std::forward<Container>(row);
    const auto delimiter_string = std::string(1, delimiter::value);
    std::copy(strings.begin(), strings.end() - 1,
              std::ostream_iterator<std::string>(stream_, delimiter_string.c_str()));
    stream_ << strings.back() << "\n";
  }

  template <typename Container> void write_rows(Container &&rows) {
    const auto &container_of_rows = std::forward<Container>(rows);
    for (const auto &row : container_of_rows) {
      write_row(row);
    }
  }
};

} // namespace csv2
